﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.Workflow;
using System.Workflow.Activities;
using Microsoft.Crm.Sdk;
using System.Workflow.ComponentModel;
using Microsoft.Crm.SdkTypeProxy;
using System.Diagnostics;
using Microsoft.Crm.Sdk.Query;
using Infusion.CRM.WF.CRM40WSX.Proxy;

namespace Infusion.CRM.WF.CRM40WSX.Activity
{
    /// <summary>
    /// This workflow activity (which can only be run against the Currency entity) updates the
    /// currency exchange rate by querying the WebserviceX.Net Currency Converter Web Service.
    /// </summary>
    [CrmWorkflowActivity("Update Exchange Rate", "WebserviceX.Net")]
    public class UpdateFxRateActivity : SequenceActivity
    {

        #region Workflow Parameters

        /// <summary>
        /// Input parameter: base currency (default currency of CRM installation).
        /// </summary>
        public static DependencyProperty BaseCurrencyProperty = DependencyProperty.Register("BaseCurrency", typeof(string), typeof(UpdateFxRateActivity));
        [CrmInput("Base Currency")]
        [CrmDefault("USD")]
        public string BaseCurrency
        {
            get
            {
                return (string)base.GetValue(BaseCurrencyProperty);
            }
            set
            {
                base.SetValue(BaseCurrencyProperty, value);
            }
        }

        /// <summary>
        /// Input parameter: Max Variation Percentage.
        /// The operation will fail if the rate has changed more than this %.
        /// </summary>
        public static DependencyProperty MaxVariationProperty = DependencyProperty.Register("MaxVariation", typeof(CrmFloat), typeof(UpdateFxRateActivity));
        [CrmInput("Max Variation Percentage")]
        [CrmDefault("10")]
        public CrmFloat MaxVariation
        {
            get
            {
                return (CrmFloat)base.GetValue(MaxVariationProperty);
            }
            set
            {
                base.SetValue(MaxVariationProperty, value);
            }
        }

        /// <summary>
        /// Input parameter: Min Variation Percentage.
        /// Rate won't be updated if the change is less than this %.
        /// </summary>
        public static DependencyProperty MinVariationProperty = DependencyProperty.Register("MinVariation", typeof(CrmFloat), typeof(UpdateFxRateActivity));
        [CrmInput("Min Variation Percentage")]
        [CrmDefault("0.1")]
        public CrmFloat MinVariation
        {
            get
            {
                return (CrmFloat)base.GetValue(MinVariationProperty);
            }
            set
            {
                base.SetValue(MinVariationProperty, value);
            }
        }

        /// <summary>
        /// Input parameter: if true create a task to log the change in rate.
        /// </summary>
        public static DependencyProperty CreateTaskProperty = DependencyProperty.Register("CreateTask", typeof(CrmBoolean), typeof(UpdateFxRateActivity));
        [CrmInput("Create Task")]
        [CrmDefault("True")]
        public CrmBoolean CreateTask
        {
            get
            {
                return (CrmBoolean)base.GetValue(CreateTaskProperty);
            }
            set
            {
                base.SetValue(CreateTaskProperty, value);
            }
        }

        /// <summary>
        /// Output Parameter: True if operation was successful. If False, Error Message will be populated.
        /// </summary>
        public static DependencyProperty SuccessProperty = DependencyProperty.Register("Success", typeof(CrmBoolean), typeof(UpdateFxRateActivity));
        [CrmOutput("Success")]
        public CrmBoolean Success
        {
            get
            {
                return (CrmBoolean)base.GetValue(SuccessProperty);
            }
            set
            {
                base.SetValue(SuccessProperty, value);
            }
        }

        /// <summary>
        /// Output Parameter: Error Message is only populated when Success == false.
        /// </summary>
        public static DependencyProperty ErrorMsgProperty = DependencyProperty.Register("ErrorMsg", typeof(string), typeof(UpdateFxRateActivity));
        [CrmOutput("Error Message")]
        public string ErrorMsg
        {
            get
            {
                return (string)base.GetValue(ErrorMsgProperty);
            }
            set
            {
                base.SetValue(ErrorMsgProperty, value);
            }
        }

        #endregion

        #region SequenceActivity

        /// <summary>
        /// Update Currency Exchange Rate
        /// </summary>
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            try
            {
                // retrieve execution context
                IContextService contextService = (IContextService)executionContext.GetService(typeof(IContextService));
                IWorkflowContext WFContext = contextService.Context;
                string currentEntityName = WFContext.PrimaryEntityName;

                if (currentEntityName == EntityName.transactioncurrency.ToString())
                {
                    if (MaxVariation == null || MaxVariation.Value < 0)
                    {
                        // argument error
                        throw new ArgumentException("Max Variation Percentage is null or negative", "MaxVariation");
                    }
                    else if (MinVariation == null || MinVariation.Value < 0)
                    {
                        // argument error
                        throw new ArgumentException("Min Variation Percentage is null or negative", "MinVariation");
                    }
                    else
                    {
                        UpdateRate(WFContext);
                    }
                }
                else
                {
                    throw new ApplicationException("Invalid entity: " + currentEntityName);
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.ToString());
                this.ErrorMsg = ex.Message;
                this.Success = new CrmBoolean(false);
            }

            return ActivityExecutionStatus.Closed;
        }

        #endregion

        /// <summary>
        /// Update Currency Exchange Rate (Body). Update this.Success and this.ErrorMsg accordingly.
        /// </summary>
        private void UpdateRate(IWorkflowContext WFContext)
        {
            ICrmService svcHandle = WFContext.CreateCrmService();

            // get currency record
            string entityName = EntityName.transactioncurrency.ToString();
            Guid recordId = WFContext.PrimaryEntityId;
            ColumnSet columns = new ColumnSet(new string[] { "isocurrencycode", "exchangerate" });
            transactioncurrency currentRecord = (transactioncurrency)svcHandle.Retrieve(entityName, recordId, columns);

            // get value from Web Service
            double newRate = new CurrencyProxy().Convert(this.BaseCurrency, currentRecord.isocurrencycode);

            if (newRate == 0.0)
            {
                throw new ApplicationException("Retrieved rate is 0.0");
            }

            // check variation
            double oldRate = decimal.ToDouble(currentRecord.exchangerate.Value);
            if (oldRate == 0.0 || Math.Abs((newRate - oldRate) / oldRate) > (MaxVariation.Value / 100.0))
            {
                this.ErrorMsg = "Rate variation (from " + oldRate +
                    " to " + newRate +
                    ") exceeds maximum variation of " + MaxVariation.Value +
                    " percent.";
                this.Success = new CrmBoolean(false);
            }
            else
            {
                if (Math.Abs(newRate / oldRate - 1.0) < MinVariation.Value / 100.0)
                {
                    // change is less than Min Variation: we do nothing
                }
                else
                {
                    // update rate
                    currentRecord.exchangerate.Value = new decimal(newRate);
                    svcHandle.Update(currentRecord);

                    if (this.CreateTask.Value)
                    {
                        // create an activity (task) to record the change in rate.
                        task tsk = new task();
                        tsk.subject = String.Format("{0} exchange rate automatically updated from {1:0.0000} to {2:0.0000}.",
                            currentRecord.isocurrencycode, oldRate, newRate);
                        Guid actId = svcHandle.Create(tsk);

                        // close it.
                        SetStateTaskRequest setStateTaskRequest = new SetStateTaskRequest();
                        setStateTaskRequest.EntityId = actId;
                        setStateTaskRequest.TaskState = TaskState.Completed;
                        setStateTaskRequest.TaskStatus = 5;	// Completed
                        SetStateTaskResponse setStateTaskResponse = (SetStateTaskResponse)svcHandle.Execute(setStateTaskRequest);
                    }
                }

                this.Success = new CrmBoolean(true);
            }
        }

    }
}
